import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Client } from 'minio';
import { v4 as uuidv4 } from 'uuid';
import * as path from 'path';

@Injectable()
export class MinioService implements OnModuleInit {
  private readonly logger = new Logger(MinioService.name);
  private minioClient: Client;
  private bucketName: string;
  private publicUrl: string;

  constructor(private configService: ConfigService) {
    // 初始化 MinIO 客户端
    this.minioClient = new Client({
      endPoint: this.configService.get<string>('MINIO_ENDPOINT') || 'localhost',
      port: parseInt(this.configService.get<string>('MINIO_PORT') || '9100'),
      useSSL: this.configService.get<string>('MINIO_USE_SSL') === 'true',
      accessKey: this.configService.get<string>('MINIO_ROOT_USER') || 'minioadmin',
      secretKey: this.configService.get<string>('MINIO_ROOT_PASSWORD') || 'minioadmin123',
    });

    this.bucketName = this.configService.get<string>('MINIO_BUCKET') || 'devtools-hub';
    this.publicUrl = this.configService.get<string>('MINIO_PUBLIC_URL') || 'http://localhost:9100';
  }

  async onModuleInit() {
    await this.ensureBucket();
  }

  /**
   * 确保存储桶存在，不存在则创建
   */
  private async ensureBucket() {
    try {
      const exists = await this.minioClient.bucketExists(this.bucketName);
      
      if (!exists) {
        this.logger.log(`创建 MinIO 存储桶: ${this.bucketName}`);
        await this.minioClient.makeBucket(this.bucketName, 'us-east-1');
        
        // 设置存储桶为公开可读（用于静态文件访问）
        const policy = {
          Version: '2012-10-17',
          Statement: [
            {
              Effect: 'Allow',
              Principal: { AWS: ['*'] },
              Action: ['s3:GetObject'],
              Resource: [`arn:aws:s3:::${this.bucketName}/*`],
            },
          ],
        };
        
        await this.minioClient.setBucketPolicy(
          this.bucketName,
          JSON.stringify(policy),
        );
        
        this.logger.log(`✅ MinIO 存储桶创建成功: ${this.bucketName}`);
      } else {
        this.logger.log(`✅ MinIO 存储桶已存在: ${this.bucketName}`);
      }
    } catch (error) {
      this.logger.error(`❌ MinIO 存储桶初始化失败:`, error);
      throw error;
    }
  }

  /**
   * 上传文件
   * @param file 文件对象
   * @param folder 文件夹（可选，如 'tools', 'articles', 'avatars'）
   * @returns 文件的完整访问URL
   */
  async uploadFile(
    file: {
      originalname: string;
      buffer: Buffer;
      size: number;
      mimetype: string;
    },
    folder: string = 'uploads',
  ): Promise<string> {
    try {
      // 生成唯一文件名
      const fileExt = path.extname(file.originalname);
      const fileName = `${folder}/${Date.now()}-${uuidv4()}${fileExt}`;

      // 上传文件到 MinIO
      await this.minioClient.putObject(
        this.bucketName,
        fileName,
        file.buffer,
        file.size,
        {
          'Content-Type': file.mimetype,
          'Cache-Control': 'public, max-age=31536000', // 缓存1年
        },
      );

      // 返回完整的公网访问URL
      const fileUrl = `${this.publicUrl}/${this.bucketName}/${fileName}`;
      
      this.logger.log(`✅ 文件上传成功: ${fileUrl}`);
      
      return fileUrl;
    } catch (error) {
      this.logger.error(`❌ 文件上传失败:`, error);
      throw new Error('文件上传失败');
    }
  }

  /**
   * 删除文件
   * @param fileUrl 文件URL
   */
  async deleteFile(fileUrl: string): Promise<void> {
    try {
      // 从URL中提取文件名
      // 例如：http://localhost:9100/devtools-hub/tools/123-uuid.jpg
      // 提取：tools/123-uuid.jpg
      const urlObj = new URL(fileUrl);
      const pathParts = urlObj.pathname.split('/');
      const fileName = pathParts.slice(2).join('/'); // 跳过前两个部分（空 + bucket名）

      await this.minioClient.removeObject(this.bucketName, fileName);
      
      this.logger.log(`✅ 文件删除成功: ${fileName}`);
    } catch (error) {
      this.logger.error(`❌ 文件删除失败:`, error);
      throw new Error('文件删除失败');
    }
  }

  /**
   * 获取文件的临时访问URL（用于私有文件）
   * @param fileName 文件名
   * @param expiry 过期时间（秒），默认7天
   * @returns 临时访问URL
   */
  async getPresignedUrl(fileName: string, expiry: number = 604800): Promise<string> {
    try {
      const url = await this.minioClient.presignedGetObject(
        this.bucketName,
        fileName,
        expiry,
      );
      
      return url;
    } catch (error) {
      this.logger.error(`❌ 获取临时URL失败:`, error);
      throw new Error('获取文件URL失败');
    }
  }

  /**
   * 列出文件
   * @param prefix 文件前缀（文件夹）
   * @param limit 数量限制
   */
  async listFiles(prefix: string = '', limit: number = 100): Promise<string[]> {
    try {
      const files: string[] = [];
      const stream = this.minioClient.listObjects(this.bucketName, prefix, true);

      return new Promise((resolve, reject) => {
        stream.on('data', (obj) => {
          if (files.length < limit) {
            files.push(`${this.publicUrl}/${this.bucketName}/${obj.name}`);
          }
        });

        stream.on('end', () => resolve(files));
        stream.on('error', reject);
      });
    } catch (error) {
      this.logger.error(`❌ 列出文件失败:`, error);
      throw new Error('列出文件失败');
    }
  }
}

